Amazon Route 53のパブリックホストゾーンの作成・削除と連動させてNotionデータベースを更新してみる
Amazon Route 53 のパブリックホストゾーン一覧を Notion データベースで一元的に管理したいと思い、自動化を試してみました。
以前にも Amazon EventBridge と Notion API を利用して、パブリックホストゾーンの作成に合わせて Notion データベースへのページ追加を自動的に行うことは試していたのですが、そのときは削除の連携までは行っていませんでした。今回は、AWS Lambda も利用して追加・削除の両方を行ってみました。
実現イメージ
実現方法として、Amazon Route 53 のホストゾーンの作成と削除に対して、Amazon EventBridge 経由で AWS Lambda を実行して Notion データベースを操作します。
Amazon Route 53 はグローバルサービスであり、イベントはバージニア北部リージョンで発生するため、設定はバージニア北部リージョンで行います。
以降は実際に試してみます。
始めに Notion API の設定とデータベースの作成を行い、その後に AWS Lambda と Amazon EventBridge の設定を行います。付随するサービスとして Systems Manager パラメータストアと IAM ロールの作成も行います。
Notion API の設定
Notion API のインテグレーション設定は次のページで行います。
今回は新しいインテグレーションを作成して下図の権限としています。コメント機能とユーザー機能は必要ないため利用しない設定としています。
作成後にトークン情報が表示されるため大切に管理します。
なお、Notion API のリファレンスは次のページで公開されています。
今回のブログで利用する API は、ページ作成時にはCreate a page
を利用し、ページの削除時にはQuery a database
とUpdate page
を利用します。
Notion データベースの作成と共有設定
Amazon Route 53 のパブリックホストゾーンを管理するためのデータベースを作成します。
項目毎の種類(プロパティ)は下表の通りです。
項目名 | 種類(プロパティ) |
---|---|
Hosted Zone ID | タイトル |
Public Hosted Zone Name | テキスト |
Account ID | テキスト |
作成日時 | 作成日時 |
作成者 | 作成者 |
最終更新日時 | 最終更新日時 |
最終更新者 | 最終更新者 |
次に、作成したインテグレーションAWS
からデータベースを操作できる設定を行います。以前は「共有」設定から行っていましたが、執筆時点ではコネクト設定から行うようになっています。作成したインテグレーションAWS
を選択します。
設定後には権限の確認を行うこともできます。
後ほど利用するため、データベース ID を控えておきます。データベース ID は Notion データベースの URL におけるnotion.so/
と?v=xxxxx
の間の 32 桁の文字列が該当します。
以上で Notion 側の設定は完了です。以降は AWS 側の設定となります。
AWS Systems Manager パラメータストアの作成
始めに、Notion API と接続するために利用するデータベース ID とトークン情報を AWS Systems Manager パラメータストアに格納します。
バージニア北部リージョンで次の設定で 2 つのパラメータを追加します。
名前 | タイプ | KMS キーソース | KMS キー ID | 値 |
---|---|---|---|---|
notion-database-id | 文字列 | - | - | データベース ID(32 桁) |
notion-token | 安全な文字列 | 現在のアカウント | alias/aws/ssm | secret_xxxxxxxxxxxxxxxxxxxxxxx |
設定後の画面です。
IAM ロールの作成
AWS Lambda にアタッチするための IAM ロールを作成します。
まず、先ほど作成したパラメータストアへのアクセス権限を設定した IAM ポリシーnotion-get-parameter-policy
を作成します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ssm:GetParameter", "Resource": [ "arn:aws:ssm:us-east-1:111122223333:parameter/notion-database-id", "arn:aws:ssm:us-east-1:111122223333:parameter/notion-token" ] } ] }
IAM ロールnotion-lambda-role
を作成して次の 2 つの IAM ポリシーをアタッチします。
- AWSLambdaBasicExecutionRole
- notion-get-parameter-policy
AWSLambdaBasicExecutionRole
は AWS 管理ポリシーであり、Amazon CloudWatch Logs に対する書き込み権限が設定されています。
IAM ロールの信頼ポリシーは次の通りです。AWS Lambda を信頼します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
AWS Lambda の設定
AWS Lambda の設定では、始めに利用するライブラリを設定したレイヤーを作成した後に Notion データベースのページを追加する関数と削除する関数を作成します。
AWS Lambda 関数は Python で作成しており、ランタイムはPython 3.8
を利用しています。
設定はバージニア北部リージョンで行います。
Lambda Layers の作成
後ほど作成する 2 つの関数においてrequests
モジュールを使用することから、事前の準備としてレイヤーの作成を行います。
始めに、レイヤーにアップロードするための zip ファイルを作成します。
$ zip -r Layer.zip python/ $ pip3 install requests -t ./python $ zip -r Layer.zip python/
レイヤーの設定を行います。上記で作成したLayer.zip
をアップロードして作成します。
以上でレイヤーの設定は完了です。
関数の作成(Notion のページ追加)
Notion データベースにページを追加する関数notion-create-page
を作成します。今回は私のローカル環境と合わせたかったこともあり、ランタイムはPython 3.8
を利用しています。IAM ロールは作成したnotion-lambda-role
を指定します。
コードは次の通りです。コードの更新後に Deploy が必要です。
import os import logging import json import boto3 import requests logger = logging.getLogger() logger.setLevel(logging.INFO) def get_secret(): paramater_name = os.environ['NOTION_TOKEN'] try: ssm = boto3.client('ssm') ssm_response = ssm.get_parameter(Name=paramater_name, WithDecryption=True) return ssm_response['Parameter']['Value'] except Exception as e: logger.error(e) raise def get_database_id(): paramater_name = os.environ['NOTION_DATABASE_ID'] try: ssm = boto3.client('ssm') ssm_response = ssm.get_parameter(Name=paramater_name, WithDecryption=False) return ssm_response['Parameter']['Value'] except Exception as e: logger.error(e) raise def lambda_handler(event, context): secret_token = get_secret() database_id = get_database_id() hostedzone_id = event['detail']['responseElements']['hostedZone']['id'].split('/')[2] hostedzone_name = event['detail']['responseElements']['hostedZone']['name'] account_id = event['account'] url = 'https://api.notion.com/v1/pages' headers = { 'Authorization': 'Bearer ' + secret_token, 'Content-Type': 'application/json; charset=UTF-8', 'Notion-Version': '2022-06-28' } payload = { 'parent': { 'type': 'database_id', 'database_id': database_id }, 'properties': { 'Hosted Zone ID': { 'title': [ { 'text': { 'content': hostedzone_id } } ] }, 'Public Hosted Zone Name': { 'rich_text': [ { 'text': { 'content': hostedzone_name } } ] }, 'Account ID': { 'rich_text': [ { 'text': { 'content': account_id } } ] } } } try: response = requests.post(url, headers=headers, data=json.dumps(payload)) response.raise_for_status() except requests.exceptions.RequestException as e: logger.error(e) raise return None
ホストゾーン ID は/hostedzone/Z01001291JZMVUEXAMPLE
の形式になっているため、split を利用してZ01001291JZMVUEXAMPLE
のみを抽出して変数hostedzone_id
に格納しています。ホストゾーン削除のイベントでは ID としてZ01001291JZMVUEXAMPLE
部分が格納されているためです。
Notion API の通信内容は次のリファレンスを基に組み立てています。
また、設定の環境変数から次の値を設定します。値はパラメータストアの名前です。
キー | 値 |
---|---|
NOTION_DATABASE_ID | notion-database-id |
NOTION_TOKEN | notion-token |
requests
を利用するためにレイヤーpython-layer
も設定(追加)します。
以上で設定は終了です。トリガーは後ほど、Amazon EventBridge において設定することにします。
関数の設定(Notion のページ削除)
Notion へのページ追加と同じ要領でページ削除を行う関数notion-archive-page
も作成します。Notion へのページ追加の関数と同様に、ランタイムはPython 3.8
、IAM ロールはnotion-lambda-role
を指定しています。
コードは次の通りです。コードの更新後に Deploy が必要です。
import os import logging import json import boto3 import requests logger = logging.getLogger() logger.setLevel(logging.INFO) def get_secret(): paramater_name = os.environ['NOTION_TOKEN'] try: ssm = boto3.client('ssm') ssm_response = ssm.get_parameter(Name=paramater_name, WithDecryption=True) return ssm_response['Parameter']['Value'] except Exception as e: logger.error(e) raise def get_database_id(): paramater_name = os.environ['NOTION_DATABASE_ID'] try: ssm = boto3.client('ssm') ssm_response = ssm.get_parameter(Name=paramater_name, WithDecryption=False) return ssm_response['Parameter']['Value'] except Exception as e: logger.error(e) raise def lambda_handler(event, context): secret_token = get_secret() database_id = get_database_id() hostedzone_id = event['detail']['requestParameters']['id'] url = 'https://api.notion.com/v1/databases/' + database_id + '/query' headers = { 'Authorization': 'Bearer ' + secret_token, 'Content-Type': 'application/json; charset=UTF-8', 'Notion-Version': '2022-06-28' } payload = { 'filter': { 'and': [ { 'property': 'Hosted Zone ID', 'title': { 'equals': hostedzone_id } } ] } } try: response = requests.post(url, headers=headers, data=json.dumps(payload)) response.raise_for_status() except requests.exceptions.RequestException as e: logger.error(e) raise else: response_data = json.loads(response.text) for result in response_data['results']: page_id = result['id'] url = 'https://api.notion.com/v1/pages/' + page_id payload = { 'parent': { 'type': 'database_id', 'database_id': database_id }, 'archived': True } try: response = requests.patch(url, headers=headers, data=json.dumps(payload)) response.raise_for_status() except requests.exceptions.RequestException as e: logger.error(e) raise return None
Notion API の通信内容は次のリファレンスを基に組み立てています。
始めに削除対象ページのページ ID を取得するため、データベースに対してホストゾーン ID が一致するページを返すクエリを実行しています。その後、取得したページに対して削除を行っています。この際に、返答された複数のページを削除できるように for 文を利用しています。ホストゾーン ID でデータベースを検索しているため 1 つのページしか返答されない想定ですが、「API としては複数のページが返答される仕様であること」「手動で重複したホストゾーン ID のページの追加が可能であること」から複数ページを削除できるようにしてみました。
設定の環境変数から次の値を設定します。値はパラメータストアの名前です。
キー | 値 |
---|---|
NOTION_DATABASE_ID | notion-database-id |
NOTION_TOKEN | notion-token |
requests
を利用するためにレイヤーpython-layer
も設定(追加)します。Notion のページ追加を行う関数と同様の設定です。
以上で設定は終了です。トリガーは後ほど、Amazon EventBridge において設定することにします。
Amazon EventBridge の設定
EventBridge の設定では、パブリックホストゾーンの作成イベントに関するルールとホストゾーンの削除イベントに関するルールを設定します。
パブリックホストゾーンの作成ルール
パブリックホストゾーンの作成に合わせて Lambda 関数notion-create-page
を実行するルールcreate-hosted-zone
を作成します。
イベントバスはdefault
として、イベントパターンは次の設定を行います。privateZone
を利用してパブリックホストゾーンの作成のみに一致するようにしています。
{ "source": ["aws.route53"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventSource": ["route53.amazonaws.com"], "eventName": ["CreateHostedZone"], "responseElements": { "hostedZone": { "config": { "privateZone": [false] } } } } }
ターゲットには Notion のページ追加を行う関数notion-create-page
を指定します。
設定後の Lambda 関数の画面です。EventBridge がトリガーとして設定されていることを確認できます。
ホストゾーンの削除ルール
ホストゾーンの削除に合わせて Lambda 関数notion-archive-page
を実行するルールdelete-hosted-zone
を作成します。
イベントバスはdefault
として、イベントパターンは次の設定を行います。ホストゾーンの作成時と異なり、パブリックホストゾーンであることを識別することができなさそうだったので、パブリックやプライベートに関係なくホストゾーンの削除と一致します。
{ "source": ["aws.route53"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventSource": ["route53.amazonaws.com"], "eventName": ["DeleteHostedZone"] } }
ターゲットには Notion のページ削除を行う関数notion-archive-page
を指定します。
設定後の Lambda 関数の画面です。EventBridge がトリガーとして設定されていることを確認できます。
イベントパターンの設定がホストゾーンの削除全てと一致していることから、プライベートホストゾーンの削除時にも Lambda が実行されてしまいます。Notion のページ削除はホストゾーン ID が一致したページを削除するため問題はないのですが、無駄に Lambda が実行されることになるため、今後の改善点となります。いまのところ良い案が思い浮かんでいません。
動作確認
最後に動作確認を行います。
Amazon Route 53 においてパブリックホストゾーンtest.example.net.
を作成してみます。
作成後に Notion データベースにページが追加されています。
続いて、上記で作成したパブリックホストゾーンtest.example.net.
を削除してみます。
Notion データベースからも削除されています。
削除されたページの履歴からも確認できます。
以上で動作確認は終了です。
さいごに
以前に書いたブログで Amazon EventBridge だけを利用したときは実現できていなかったパブリックホストゾーンの作成とホストゾーンの削除の両方を Notion データベースに連携する方法を試してみました。
コードの内容やプライベートホストゾーンの削除時にも Notion のページ削除を行う Lambda 関数が実行される点など、まだ改善の余地はありますが、追加・削除の両方を連携できることを確認できたことで色々できることが広がりそうです。他には購入・移管したドメインの管理や EC2 インスタンスの一覧管理などもできそうです。
以上、このブログがどなたかのご参考になれば幸いです。